﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using SageKing.Features.Contracts;
using SageKing.OpenIddict.UI.Identity.Core;
using SageKing.OpenIddict.UI.Identity.Infrastructure;
using SageKing.OpenIddict.UI.Infrastructure.Data;
using SageKing.OpenIddict.UI.Infrastructure.Features;

namespace SageKing.Extensions;

public static class ModuleExtensions
{
    public static IModule UseOpenIddictUIIdentityInfrastructureServer(this IModule module, Action<OpenIddictUIIdentityInfrastructureFeature>? configure = default)
    {
        module.Configure<OpenIddictUIIdentityInfrastructureFeature>(feature =>
        {
            configure?.Invoke(feature);
        });

        module.UseIceMediatR(o => o.MediatRServiceConfiguration += a =>
        {
            a.RegisterServicesFromAssembly(typeof(OpenIddictUIIdentityInfrastructureFeature).Assembly);
        });


        return module;
    }

    public static OpenIddictBuilder AddUIIdentityStore<TApplicationUser>(
    this OpenIddictBuilder builder,
    Action<OpenIddictUIIdentityStoreOptions> storeOptionsAction = null
  ) where TApplicationUser : AppUser, new() => AddUIIdentityStore<TApplicationUser, long>(builder, storeOptionsAction);

    public static OpenIddictBuilder AddUIIdentityStore<TApplicationUser, TKey>(
      this OpenIddictBuilder builder,
      Action<OpenIddictUIIdentityStoreOptions> storeOptionsAction = null
    )
      where TKey : IEquatable<TKey>
      where TApplicationUser : TenantIdentityUser<TKey>, new()
    {
        builder.Services.AddInfrastructureServices<TApplicationUser, TKey>();

        builder.Services
          .AddOpenIddictUIIdentityStore<OpenIddictUIIdentityContext>(storeOptionsAction);

        return builder;
    }

    internal static IServiceCollection AddInfrastructureServices<TApplicationUser, TKey>(
        this IServiceCollection services
      )
        where TKey : IEquatable<TKey>
        where TApplicationUser : IdentityUser<TKey>, new()
    {
        // core services
        services.AddOpenIddictUIIdentityCoreServices<TApplicationUser, TKey>();

        // own services
        services.AddScoped(typeof(IAsyncRepository<,>), typeof(EfRepository<,>));
        services.AddTransient<IClaimTypeRepository, ClaimTypeRepository<OpenIddictUIIdentityContext>>();
        services.AddTransient<IAccountService, AccountService<TApplicationUser, TKey>>();

        return services;
    }

    internal static IServiceCollection AddOpenIddictUIIdentityStore<TContext>(
      this IServiceCollection services,
      Action<OpenIddictUIIdentityStoreOptions> storeOptionsAction = null)
      where TContext : DbContext, IOpenIddictUIIdentityContext
    {
        var options = new OpenIddictUIIdentityStoreOptions();
        services.AddSingleton(options);
        storeOptionsAction?.Invoke(options);

        if (options.ResolveDbContextOptions != null)
        {
            services.AddDbContext<TContext>(options.ResolveDbContextOptions);
        }
        else
        {
            services.AddDbContext<TContext>(dbCtxBuilder =>
            {
                options.OpenIddictUIIdentityContext?.Invoke(dbCtxBuilder);
            });
        }

        services.AddScoped<IOpenIddictUIIdentityContext, TContext>();

        return services;
    }

}
